package sf.database;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sf.core.DBObject;
import sf.database.dao.DBClient;
import sf.database.support.EntityScanner;
import sf.database.template.TemplateRender;
import sf.database.template.TemplateType;
import sf.database.util.OrmValueUtils.BeanValueType;
import sf.tools.StringUtils;

import java.util.Arrays;
import java.util.Map;

public class OrmConfig {
    private static final Logger logger = LoggerFactory.getLogger(OrmConfig.class);

    protected static OrmConfig instance = new OrmConfig();

    public static OrmConfig getInstance() {
        return instance;
    }

    public OrmConfig() {

    }

    /**
     * 指定扫描若干包,配置示例如下—— <code><pre>
     * &lt;list&gt;
     *  &lt;value&gt;org.sf.test&lt;/value&gt;
     *  &lt;value&gt;org.sf.entity&lt;/value&gt;
     * &lt;/list&gt;
     * </pre></code>
     */
    protected String[] packagesToScan;

    /**
     * 对配置了包扫描的路径进行增强检查，方便单元测试
     */
    protected boolean enhanceScanPackages = true;

    /**
     * 扫描已知的若干注解实体类，配置示例如下—— <code><pre>
     * &lt;list&gt;
     *  &lt;value&gt;org.sf.testp.jta.Product&lt;/value&gt;
     *  &lt;value&gt;org.sf.testp.jta.Users&lt;/value&gt;
     * &lt;/list&gt;
     * </pre></code>
     */
    protected String[] annotatedClasses;


    /**
     * 扫描到实体后，如果数据库中不存在，是否建表 <br>
     * 默认开启
     */
    protected boolean createTable = true;

    /**
     * 扫描到实体后，如果数据库中存在对应表，是否修改表 <br>
     * 默认开启
     */
    protected boolean alterTable = true;

    /**
     * 扫描到实体后，如果准备修改表，如果数据库中的列更多，是否允许删除列 <br>
     * 默认关闭
     */
    protected boolean allowDropColumn;


    //以下为新增.
    /**
     * 是否执行表结构初始化
     */
    private boolean initScan = true;
    /**
     * 批处理数量
     */
    protected int batchSize = 100;
    /**
     * sql日志开关
     */
    protected boolean showSql = true;
    /**
     * 是否打印方法调用,在showSql=true的情况下才有用
     */
    protected boolean showCallChain = false;
    /**
     * 是否使用系统打印日志
     */
    protected boolean useSystemPrint = true;

    /**
     * bean转换方式
     * 快速设置bean方法 使用unsafe实现
     */
    protected BeanValueType beanValueType = BeanValueType.method;

    /**
     * 是否使用tail获取额外属性.
     */
    protected boolean useTail = false;


    /**
     * 是否开启sql模板调试模式
     */
    protected boolean sqlTemplateDebug = false;
    /**
     * sql模板位置
     */
    protected String sqlTemplatePath = "classpath*:sql/**/*.sql";

    /**
     * 具体的sql模板位置,如enjoy,freemarker,mybatis等可以单独配置
     */
    protected Map<String, String> sqlTemplatePaths;
    /**
     * 是否开启流式迭代(默认关闭)
     */
    public boolean openStreamIterator = false;

    /**
     * 使用的sql默认模板类型
     */
    protected TemplateType sqlTemplateType = TemplateType.enjoy;

    /**
     * 是否需要检查类增强
     */
    protected boolean checkEnhancement = true;

    /**
     * 自定义方言查找
     */
    protected String lookDialectClass;

    /**
     * 初始化方法
     */
    public void init(DBClient dbClient) {
        initParam(this);
        initDB(this, this.packagesToScan, dbClient);
    }

    /**
     * 设置参数
     * @param config
     */
    public static void initParam(OrmConfig config) {
        if (StringUtils.isNotBlank(config.getSqlTemplatePath())) {
            TemplateRender.defaultPath = StringUtils.split(config.getSqlTemplatePath(), ",");
        }
    }

    /**
     * 扫描类允许传入
     * @param config
     */
    public static void initDB(OrmConfig config, String[] packagesToScans, DBClient dbClient) {
        if (packagesToScans != null || config.getAnnotatedClasses() != null) {
            EntityScanner qe = new EntityScanner();
//            if (transactionMode == TransactionMode.JTA) {
            // JTA事务下，DDL语句必须在已启动后立刻就做，迟了就被套进JTA的事务中，出错。
            qe.setCheckSequence(false);
//            }
            qe.setImplClasses(DBObject.class);
            qe.setAllowDropColumn(config.isAllowDropColumn());
            qe.setAlterTable(config.isAlterTable());
            qe.setCreateTable(config.isCreateTable());
            qe.setDbClient(dbClient);
            if (config.getAnnotatedClasses() != null)
                qe.registeEntity(config.getAnnotatedClasses());
            if (packagesToScans != null) {
                qe.setPackageNames(packagesToScans);
                logger.info("Starting scan orm entity from package: {}", Arrays.toString(packagesToScans));
                qe.doScan();
            }
            qe.finish();
        }
    }

    public String[] getPackagesToScan() {
        return packagesToScan;
    }

    public void setPackagesToScan(String[] packagesToScan) {
        this.packagesToScan = packagesToScan;
    }

    public boolean isEnhanceScanPackages() {
        return enhanceScanPackages;
    }

    public void setEnhanceScanPackages(boolean enhanceScanPackages) {
        this.enhanceScanPackages = enhanceScanPackages;
    }

    public String[] getAnnotatedClasses() {
        return annotatedClasses;
    }

    public void setAnnotatedClasses(String[] annotatedClasses) {
        this.annotatedClasses = annotatedClasses;
    }

    public boolean isCreateTable() {
        return createTable;
    }

    public void setCreateTable(boolean createTable) {
        this.createTable = createTable;
    }

    public boolean isAlterTable() {
        return alterTable;
    }

    public void setAlterTable(boolean alterTable) {
        this.alterTable = alterTable;
    }

    public boolean isAllowDropColumn() {
        return allowDropColumn;
    }

    public void setAllowDropColumn(boolean allowDropColumn) {
        this.allowDropColumn = allowDropColumn;
    }

    public boolean isInitScan() {
        return initScan;
    }

    public void setInitScan(boolean initScan) {
        this.initScan = initScan;
    }

    public boolean isShowSql() {
        return showSql;
    }

    public void setShowSql(boolean showSql) {
        this.showSql = showSql;
    }

    public boolean isShowCallChain() {
        return showCallChain;
    }

    public void setShowCallChain(boolean showCallChain) {
        this.showCallChain = showCallChain;
    }

    public BeanValueType getBeanValueType() {
        return beanValueType;
    }

    public void setBeanValueType(BeanValueType beanValueType) {
        this.beanValueType = beanValueType;
    }

    public boolean isUseTail() {
        return useTail;
    }

    public void setUseTail(boolean useTail) {
        this.useTail = useTail;
    }

    public String getSqlTemplatePath() {
        return sqlTemplatePath;
    }

    public void setSqlTemplatePath(String sqlTemplatePath) {
        this.sqlTemplatePath = sqlTemplatePath;
    }

    public Map<String, String> getSqlTemplatePaths() {
        return sqlTemplatePaths;
    }

    public void setSqlTemplatePaths(Map<String, String> sqlTemplatePaths) {
        this.sqlTemplatePaths = sqlTemplatePaths;
    }

    public boolean isSqlTemplateDebug() {
        return sqlTemplateDebug;
    }

    public void setSqlTemplateDebug(boolean sqlTemplateDebug) {
        this.sqlTemplateDebug = sqlTemplateDebug;
    }

    public int getBatchSize() {
        return batchSize;
    }

    public void setBatchSize(int batchSize) {
        this.batchSize = batchSize;
    }

    public boolean isOpenStreamIterator() {
        return openStreamIterator;
    }

    public void setOpenStreamIterator(boolean openStreamIterator) {
        this.openStreamIterator = openStreamIterator;
    }

    public TemplateType getSqlTemplateType() {
        return sqlTemplateType;
    }

    public void setSqlTemplateType(TemplateType sqlTemplateType) {
        this.sqlTemplateType = sqlTemplateType;
    }

    public boolean isUseSystemPrint() {
        return useSystemPrint;
    }

    public void setUseSystemPrint(boolean useSystemPrint) {
        this.useSystemPrint = useSystemPrint;
    }

    public boolean isCheckEnhancement() {
        return checkEnhancement;
    }

    public void setCheckEnhancement(boolean checkEnhancement) {
        this.checkEnhancement = checkEnhancement;
    }

    public String getLookDialectClass() {
        return lookDialectClass;
    }

    public void setLookDialectClass(String lookDialectClass) {
        this.lookDialectClass = lookDialectClass;
    }
}
